import random

from django import forms
from django.conf import settings
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError

from web.form.bootstrap import BootStrapForm

from django_redis import get_redis_connection

from web import models
from utils import encrypt




class RegisterModelForm(BootStrapForm, forms.ModelForm):
    """ 优先级 进行先后顺序验证 """

    password = forms.CharField(label='密码', min_length=6, max_length=16,
                               error_messages={'min_length': '密码长度不能小于6位', 'max_length': '密码长度不能大于16位'},
                               widget=forms.PasswordInput(), )
    confirm_password = forms.CharField(label="确认密码", min_length=6, max_length=16,
                                       error_messages={'min_length': '密码长度不能小于6位', 'max_length': '密码长度不能大于16位'},
                                       widget=forms.PasswordInput(), )
    mobile_phone = forms.CharField(label='手机号码', max_length=11,
                                   validators=[RegexValidator(r'^1[3|4|5|6|7|8|9]\d{9}$', '手机号码格式错误')], )

    code = forms.CharField(label='验证码', max_length=6, widget=forms.TextInput())

    class Meta:
        model = models.UserInfo
        fields = ['username', 'email', 'password', 'confirm_password', 'mobile_phone', 'code']

    def clean_username(self):
        username = self.cleaned_data['username']
        exists = models.UserInfo.objects.filter(username=username).exists()
        if exists:
            raise ValidationError('用户名已存在')
            # self.add_error("username", "用户名已存在"))
        return username

    def clean_email(self):
        email = self.cleaned_data['email']
        exists = models.UserInfo.objects.filter(email=email).exists()
        if exists:
            raise ValidationError('邮箱已存在')
        return email

    def clean_password(self):
        password = self.cleaned_data['password']

        # 加密返回
        return encrypt.md5(password)

    def clean_confirm_password(self):
        pwd = self.cleaned_data.get("password")
        confirm_pwd = encrypt.md5(self.cleaned_data['confirm_password'])

        if pwd != confirm_pwd:
            raise ValidationError('两次密码不一致')

        return confirm_pwd

    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data['mobile_phone']
        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        if exists:
            raise ValidationError('手机号已存在')

        return mobile_phone

    def clean_code(self):
        code = self.cleaned_data['code']

        mobile_phone = self.cleaned_data.get("mobile_phone")
        if not mobile_phone:
            return code
        conn = get_redis_connection()
        redis_code = conn.get(mobile_phone)
        if not redis_code:
            raise ValidationError('验证码已过期')

        redis_str_code = redis_code.decode('utf-8')

        if code.strip() != redis_str_code:
            raise ValidationError('验证码错误')

        return code


class SendSmsForm(forms.Form):
    mobile_phone = forms.CharField(label='手机号码', validators=[RegexValidator(r'^1[3|4|5|6|7|8|9]\d{9}$', '手机号码格式错误')], )

    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    def clean_mobile_phone(self):
        """手机号检验的钩子"""
        mobile_phone = self.cleaned_data['mobile_phone']

        # 判断短信模板是否有问题
        tpl = self.request.GET.get('tpl')
        template_id = settings.TENCENT_SMS_TEMPLATE.get(tpl)
        if not template_id:
            """ self.add_error('mobile_phone') """
            raise ValidationError('短信模板不存在')

        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        if tpl == "login":
            if not exists:
                raise ValidationError('手机号不存在')
        else:
            # 校验数据库中是否有该手机号
            if exists:
                raise ValidationError('手机号已存在')
        code = random.randrange(1000, 9999)

        # 发送短信
        print("这个是验证码:", code)

        conn = get_redis_connection()
        conn.set(mobile_phone, code, ex=60)

        context = {
            "mobile_phone":mobile_phone,
            'code':code
        }
        return context


class LoginForm(BootStrapForm, forms.Form):
    username = forms.CharField(label="邮箱或手机号")
    password = forms.CharField(label="密码", widget=forms.PasswordInput(render_value=True))  # 渲染表单时，表单的值会显示在页面上
    code = forms.CharField(label="图片验证码")

    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    def clean_password(self):
        pwd = self.cleaned_data['password']
        # 加密 & 返回
        return encrypt.md5(pwd)

    def clean_code(self):
        """ 钩子  图片验证码是否正确 """
        code = self.cleaned_data['code']
        # 去session中获取验证码
        session_code = self.request.session.get('image_code')
        if not session_code:
            raise ValidationError('验证码已过期,请重新获取')

        if code.strip().lower() != session_code.strip().lower():
            raise ValidationError('验证码错误')

        return code


class LoginSMSForm(BootStrapForm, forms.Form):
    mobile_phone = forms.CharField(label='手机号', validators=[RegexValidator(r'1[3456789]\d{9}$', '手机号码格式错误')], )

    code = forms.CharField(label='验证码',widget=forms.TextInput())

    def clean_mobile_phone(self):
        mobile_phone = self.cleaned_data['mobile_phone']
        exists = models.UserInfo.objects.filter(mobile_phone=mobile_phone).exists()
        if not exists:
            raise ValidationError('手机号不存在')
        return mobile_phone

    def clean_code(self):
        code = self.cleaned_data['code']
        mobile_phone = self.cleaned_data['mobile_phone']

        #手机号不存在，则验证码无需校验
        if not mobile_phone:
            return  code

        conn = get_redis_connection()
        redis_code = conn.get(mobile_phone)  #根据获取的手机号去获取验证码

        if not redis_code:
            raise ValidationError('验证码已过期或未发送,请重新获取')

        redis_str_code = redis_code.decode("utf-8")

        if code.strip() != redis_str_code:
            raise ValidationError('验证码错误')

        return code
